classdef uController
    % uController class enables control of uController via USB.
    % uController controls attenuators, CLK generators, 
     
    properties
        address
        conn
    end
    
    properties(Constant)
        EEPROM_BYTE_NUMBER = 4096;
        EEPROM_READ_ADDRESS = hex2dec('A1');
        EEPROM_WRITE_ADDRESS = hex2dec('A0');
        EXPANDER_READ_ADDRESS = hex2dec('7D');
        EXPANDER_WRITE_ADDRESS = hex2dec('7C');
        LTC2493_READ_ADDRESS = hex2dec('69');
        LTC2493_WRITE_ADDRESS = hex2dec('68');
        ACK_WAITING_TIME = 10000;
    end
    
    methods(Static)
        function test()
            try
                uC = uController('COM4');
                % setting low attenuation
                uC.set_attenuation(2, 0);   % To uDWC dedicated file %
                
                % programming the buffer
                %uC.set_serial_buffer(0, [0 0 0 0 0 0 0 0]); % To uDWC dedicated file %
            catch error
                disp(error.identifier);
                disp(error.message);
                for s=1:length(error.stack)
                    disp(error.stack(s).name);
                    disp(error.stack(s).line);
                end
            end
            uC.delete();
        end
    end
        
    methods
        function this = uController(address)
            this.address = address;
            connections = instrfind('Type', 'serial', 'Port', address, 'Tag', '');
            % Create the serial port object if it does not exist
            % otherwise use the object that was found.
            if isempty(connections)
                this.conn = serial(address);
            else
                fclose(connections);
                this.conn = connections(1);
            end
            this.conn.Parity = 'none';
            this.conn.DataBits = 8;
            this.conn.BaudRate = 9600;
            this.conn.StopBits = 1;
            fopen(this.conn);
            
            this.set_pin_output('B07',0);
            this.set_pin_output('A03',1);
            this.set_pin_output('A00',1);
            this.set_pin_output('C03',1);
            this.set_pin_output('C02',1);
        end
        
        function delete(this)
            fclose(this.conn);
            delete(this.conn);
        end
        
        function set_RF_channel(this, channel)  % To uDWC dedicated file %
            % used where the channels are changed manually
%             h = msgbox(sprintf('Change the RF channel to %d', channel),...
%                 'RF channel', 'warn');
%             uiwait(h);
        end
        
        function set_IF_channel(this, channel)  % To uDWC dedicated file %
%             h = msgbox(sprintf('Change the IF channel to %d', channel),...
%                 'IF channel', 'warn');
%             uiwait(h);
        end
        
        function set_serial_buffer(this, buffer, output)
            if buffer == 0
                shift_pin = 'D04';
                in_pin = 'D05';
                clr_pin = 'D06';
                set_pin = 'D07';
            elseif buffer == 1
                shift_pin = 'D01';
                in_pin = 'D00';
                clr_pin = 'D02';
                set_pin = 'D03';
            else
                WrongBuff = MException('uController:set_serial_buffer',...
                    'The chosen buffer does not exist');
                throw(WrongBuff);
            end
            this.set_pin_output(clr_pin, 0);
            this.set_pin_output(clr_pin, 1);
            this.set_pin_output(shift_pin, 0);
            this.set_pin_output(set_pin, 0);
            for i = length(output):-1:1
                this.set_pin_output(in_pin, output(i));
                this.set_pin_output(shift_pin, 1);
                this.set_pin_output(shift_pin, 0);
            end
            this.set_pin_output(set_pin,1);
            this.set_pin_output(set_pin,0);
        end
        
        function set_attenuation(this, channel, att)    % To uDWC dedicated file %
            % Sets the attenuation of the specified channel. The
            % attenuation can be in range of 0 to 31 dB with a step of 0.5
            % dB.
            
            % settings for the AMC-TESt board
            att = round(63 - 2*att);
            if att < 1 || att > 63
                WrongAtt = MException('uController:set_attenuation','The set attenuation value is out of range');
                throw(WrongAtt);
            end
            if channel < 0 || channel > 9
                WrongChannel = MException('uController:set_attenuation',...
                    'The specified channel do not exist');
                throw(WrongChannel);
            end
%             sending the attenuation value
            this.set_pin_output('B07',0);
            this.SPI_write(att);
            
            bits = dec2bin(channel,4);
            % setting the specified attenuator
            this.set_pin_output('A03',str2double(bits(4)));
            this.set_pin_output('A00',str2double(bits(3)));
            this.set_pin_output('C03',str2double(bits(2)));
            this.set_pin_output('C02',str2double(bits(1)));

            this.set_pin_output('B07',1);
            this.set_pin_output('B07',0);
            % coming back to the initial state
            this.set_pin_output('A03',1);
            this.set_pin_output('A00',1);
            this.set_pin_output('C03',1);
            this.set_pin_output('C02',1);
            
            this.set_pin_output('B07',1);
            this.set_pin_output('B07',0);
              
        end
        
        function set_configuration(this, conf, RF_source, LO_source)
%             if strcmp(conf,'NWA')
%                 h = msgbox('Change the configuration to NWA (port 1 for RF, port 2 for LO)',...
%                     'RF channel', 'warn');
%                 uiwait(h);
%             elseif strcmp(conf,'SAN')
%                 h = msgbox('Change the configuration to signal analyzer (port 2)',...
%                     'RF channel', 'warn');
%                 uiwait(h);
%             end
        end
        
        function temperature = get_temperature(this) % To uDWC dedicated file %
            tmp = 0;
            % External input 50Hz/60Hz Rejection, 1x speed
            % Read channel 1
            this.adcLtc2493Write(hex2dec('B880'));
            tmp = this.adcLtc2493Read(); % This still to be calculated frm 32-bit output data
            sign = bitand(tmp,hex2dec('80000000'));
            tmp = bitand(tmp,hex2dec('7FFFFFFF'));
            ToTemp = tmp / 2147483648.0;%0x80000000;
            if(sign == 1)
                ToTemp = 0.5 * 3.3 * (1 - ToTemp);
            else
                ToTemp = 0.5 * 3.3 * (1 + ToTemp);
            end
            Temp = 25 + (ToTemp - 1.4) / 5e-3;
            temperature = 0;
        end
        
        function LO_detection_state = get_LO_detection_state(this) % To uDWC dedicated file %
            tmp = 0;
            % External input 50Hz/60Hz Rejection, 1x speed
            % Read channel 0
            this.adcLtc2493Write(hex2dec('B080'));
            tmp = this.adcLtc2493Read(); % This still to be calculated frm 32-bit output data
            LO_detection_state = 1;
        end
        
        function set_pin_output(this, pin, value, map)
            if nargin < 4
                map = 0;
            end
            if map == 1
                pin = map_pin(pin);
            end
            str = sprintf('P%s%d',pin,value);
            fprintf(this.conn,str);
        end
        
        function pin_state = get_pin_input(this, pin, map)
            if nargin < 3
                map = 0;
            end
            if map == 1
                pin = map_pin(pin);
            end
            str = sprintf('P%s?',pin);
            fprintf(this.conn,str);
            pin_state = fscanf(this.conn,'%c',1);
        end
        
        function SPI_write(this, data)
            fprintf(this.conn, 'SPIW%c',data);
        end
        
        function data = SPI_read(this)
            fprintf(this.conn, 'SPIR?');
            data = fscanf(this.conn,'%c',1);
        end

        function ack = i2cWriteAddress(this, data)
            fprintf(this.conn, 'I2CA%c',data);
            ack = fscanf(this.conn,'%c',1);
        end
        
        function i2cWrite(this, data)
            fprintf(this.conn, 'I2CW%c',data);
        end
        
        function i2cWriteAndStop(this, data)
            fprintf(this.conn, 'I2CX%c',data);
        end
        
        function data = i2cRead(this)
            fprintf(this.conn, 'I2CR?');
            data = fscanf(this.conn, '%c',1);
        end
        
        function data = i2cReadAndStop(this)
            fprintf(this.conn, 'I2CS?');
            data = fscanf(this.conn, '%c',1);
        end           
                
        function data = expanderReadByte(this)
            this.i2cWriteAddress(uController.EXPANDER_READ_ADDRESS);
            data = this.i2cReadAndStop();
        end
        
        function expanderWriteByte(this, data)
            this.i2cWriteAddress(uController.EXPANDER_WRITE_ADDRESS);
            this.i2cWriteAndStop(data);
        end
        
        function data = eepromReadByte(this, pageAddress)
            this.i2cWriteAddress(uController.EEPROM_WRITE_ADDRESS); 
            this.i2cWrite(bitand(pageAddress,hex2dec('FF00'))/256); %Write older bits
            this.i2cWrite(bitand(pageAddress,hex2dec('00FF')));
            this.i2cWriteAddress(uController.EEPROM_READ_ADDRESS);
            data = this.i2cReadAndStop();
        end
        
        function data = eepromReadMultipleByte(this, memmoryAddress, nBytes)
            % Return the n-matrix with n-Bytes
            data = zeros(nBytes);
            this.i2cWriteAddress(uController.EEPROM_WRITE_ADDRESS);
            this.i2cWrite(bitand(memmoryAddress,hex2dec('FF00'))/256); %Write older bits
            this.i2cWriteAndStop(bitand(memmoryAddress,hex2dec('00FF')));
            this.i2cWriteAddress(uController.EEPROM_READ_ADDRESS);
            for nByte = 1 : nBytes - 1
                data(nByte) = this.i2cRead();    
            end
            data(nBytes) = this.i2cReadAndStop();
        end
        
        function data = eepromReadAll(this)
            data = this.eepromRealMultipleByte(0,uController.EEPROM_BYTE_NUMBER);
        end
        
        function eepromWriteByte(this, pageAddress, data)
            this.i2cWriteAddress(uController.EEPROM_WRITE_ADDRESS); 
            this.i2cWrite(bitand(pageAddress,hex2dec('FF00'))/256); %Write older bits
            this.i2cWrite(bitand(pageAddress,hex2dec('00FF')));
            this.i2cWriteAndStop(uint8(data));
        end
        
        function eepromWritePage(this, pageAddress, data)
            % Function give ability to address only page. 
            % Any other address will 
            pageAddress = bitand(pageAddress,hex2dec('FFE0'));
            this.i2cWriteAddress(uController.EEPROM_WRITE_ADDRESS); 
            this.i2cWrite(bitand(pageAddress,hex2dec('FF00'))/256); %Write older bits
            this.i2cWrite(bitand(pageAddress,hex2dec('00FF')));
            
            for nByte = 1:31
                this.i2cWrite(mod(data,256));
                data = floor(data / 256); 
            end
            this.i2cWriteAndStop(mod(data,256));
        end
        
        function data = adcLtc2493Read(this)
            waitingTime = uController.ACK_WAITING_TIME;
            
            while(waitingTime > 0)
                if(0 == this.i2cWriteAddress(uController.LTC2493_READ_ADDRESS))
                    data = this.i2cRead() * 2^24;
                    data = data + this.i2cRead() * 2^16;
                    data = data + this.i2cRead() * 2^8;
                    data = data + this.i2cReadAndStop();
                    waitingTime = 0;
                else
                    data = 0;
                end
                waitingTime = waitingTime - 1;
            end  
            
        end
        
        function adcLtc2493Write(this, data)
            waitingTime = uController.ACK_WAITING_TIME;
            
            while(waitingTime > 0 ),
                if(0 == this.i2cWriteAddress(uController.LTC2493_WRITE_ADDRESS)),
                    this.i2cWrite(bitand(data,hex2dec('FF00'))/256); %Write older bits
                    this.i2cWriteAndStop(bitand(data,hex2dec('00FF')));
                    waitingTime = 0;
                end
                waitingTime = waitingTime - 1;
            end
            
        end
    end
    
    methods (Access = private)
        function write(this, str)
            for i=1:length(str)
                fprintf(this.conn, str(i));
            end
        end
        
        
        function mapped_pin = map_pin(pin)
            switch(pin)
                case 'P01'
                    mapped_pin ='B05';     
                case 'P02'
                    mapped_pin ='B08';
                case 'P03'
                    mapped_pin = 'B09';
                case 'P04'
                    mapped_pin = 'E00';
                case 'P05'
                    mapped_pin = 'E01';
                case 'P06'
                    mapped_pin = 'E02';
                case 'P07'
                    mapped_pin = 'E03';
                case 'P08'
                    mapped_pin = 'E04';
                case 'P09'
                    mapped_pin = 'E05';
                case 'P10'
                    mapped_pin = 'E06';
                case 'P11'
                    mapped_pin = 'C13';
                case 'P12'
                    mapped_pin = 'C14';
                case 'P13'
                    mapped_pin = 'C15';
                case 'P14'
                    mapped_pin = 'C00';
                case 'P15'
                    mapped_pin = 'C02';   
                case 'P16'
                    mapped_pin = 'C03';
                case 'P17'
                    mapped_pin = 'A00';
                case 'P18'
                    mapped_pin = 'A03';
                case 'P19'
                    mapped_pin = 'C04';
                case 'P20'
                    mapped_pin = 'C05';
                case 'P21'
                    mapped_pin = 'B00';
                case 'P22'
                    mapped_pin = 'B01';
                case 'P23' 
                    mapped_pin = 'E07';
                case 'P24'
                    mapped_pin = 'E08';
                case 'P25'
                    mapped_pin = 'E09';
                case 'P26'
                    mapped_pin = 'E10';
                case 'P27'
                    mapped_pin = 'D07';
                case 'P28'
                    mapped_pin = 'D06';
                case 'P29'
                    mapped_pin = 'D05';
                case 'P30'
                    mapped_pin = 'D04';
                case 'P31'
                    mapped_pin = 'D03';
                case 'P32'
                    mapped_pin = 'D02';
                case 'P33'
                    mapped_pin = 'D01';
                case 'P34'
                    mapped_pin = 'D00';
                case 'P35'
                    mapped_pin = 'A08';
                case 'P36'
                    mapped_pin = 'C08';
                case 'P37'
                    mapped_pin = 'C07';
                case 'P38'
                    mapped_pin = 'C06';
                case 'P39'
                    mapped_pin = 'D15';
                case 'P40'
                    mapped_pin = 'D14';
                case 'P41'
                    mapped_pin = 'D13';
                case 'P42'
                    mapped_pin = 'D12';
                case 'P43'
                    mapped_pin = 'D11';
                case 'P44'
                    mapped_pin = 'B15';
                case 'P45'
                    mapped_pin = 'B14';
                case 'P46'
                    mapped_pin = 'B10';
                case 'P47'
                    mapped_pin = 'E15';
                case 'P48'
                    mapped_pin = 'E14';
                case 'P49'
                    mapped_pin = 'E13';
                case 'P50'
                    mapped_pin = 'E12';
                case 'P51'
                    mapped_pin = 'E11';
                otherwise 
                    WrongPin = MException('uC:map_pin', 'Selected pin does not exist');
                    throw(WrongPin);
            end
        end
    end
    
end

